#include "TextCalculator.h"

TextCalculator::TextCalculator(void)
{
}


TextCalculator::~TextCalculator(void)
{
	userInput.clear();
	temporaryString.clear();
}

void TextCalculator::begin(void)
{
	do
	{
		userInput.clear();
		temporaryString.clear();

		cout << " >>> ";
		getline(cin, userInput);
		cout << endl;

		userInput = removeSpaces(userInput);
		userInput = validateInput(userInput);

		if(error(userInput))
			continue;
		else if(!exit(userInput))
		{
			temporaryString = parentheses(userInput);

			if(error(temporaryString))
				continue;

			if(divideByZeroCheck(temporaryString))	
				continue;
			
			temporaryString = exponents(temporaryString);
			temporaryString = multiplyDivide(temporaryString);

			if(error(temporaryString))
				continue;

			if(divideByZeroCheck(temporaryString))	
				continue;

			temporaryString = addSubtract(temporaryString);

			if(error(temporaryString))
				continue;

			cout << endl;
			cout << "\tSolution: ";
			displayList(temporaryString);
			cout << endl << endl;
		}

	} while(userInput != "EXIT");
}

void TextCalculator::displayList(string input)
{
	cout << input;
}

string TextCalculator::addSubtract(string input)
{
	string temp = searchString(input, '+', '-');
	return temp;
}

string TextCalculator::multiplyDivide(string input)
{
	string temp = searchString(input, '*', '/');
	return temp;
}

string TextCalculator::parentheses(string input)
{
	string temp = searchString(input, '(');
	return temp;
}

string TextCalculator::searchString(string input, char operator1, char operator2)
{
	int count, center, left, right, erasePosition, eraseCount, decision;
	double arg1, arg2;
	string leftString, rightString, tempString;
	bool notFound = false;

	int stringSize;

	do
	{
		count = 0;
		stringSize = input.size();
		center = left = right = erasePosition = eraseCount = decision = 0;
		arg1 = arg2 = 0;
		leftString.clear();
		rightString.clear();
		tempString.clear();

		if(input.empty())
			return "ERROR";

		if(input[count] == '-')
			count++;

		for(; count < stringSize && input[count] != operator1 && input[count] != operator2; count++);
		
		if(count == stringSize && input[count-1] != operator1 &&input[count-1] != operator2)
		{
			notFound = true;
			break;
		}

		center = count;
		count--;

		if(input[center] == '+')
			decision = 1;
		else if(input[center] == '-')
			decision = 2;
		else if(input[center] == '*')
			decision = 3;
		else if(input[center] == '/')
			decision = 4;

		for(; count >= 0 && input[count] != '+' && input[count] != '-' && input[count] != '/' && input[count] != '*'; count--);
		
		left = count;

		if(left == 0)
		{
			int arraySize = (center - left);
			left = (center - left);

			for(int x = 0; x < arraySize; x++)
			{
				tempString = input[x];
				leftString.append(tempString);
			}
		}
		else
		{
			int arraySize = (center - (left+1));

			for(int x = (left+1); x < (arraySize+left+1); x++)
			{
				tempString = input[x];
				leftString.append(tempString);
			}

			left = (center - (left+1));
		}

		// Test contents of leftString to make sure
		// it is valid input.
		
		if(!isDouble(leftString))
		{
			return "ERROR";
		}

		// End testing of leftString.

		arg1 = atof(leftString.c_str());

		count = (center+1);

		// Allows rightString to be a negative number.
		if(input[count] == '-')
			count++;
		// End check for negative number.

		for(; count < stringSize && input[count] != '+' && input[count] != '-' && input[count] != '/' && input[count] != '*'; count++);

		right = count;

		if(count == (stringSize-1))
		{
			int arraySize = (right - center);
			
			for(int x = right; x > center; x--)
			{
				tempString = input[x];
				rightString.append(tempString);
			}

			right = (right - center);
		}
		else
		{
			int arraySize = ((right-1) - center);

			for(int x = (right-1); x > center; x--)
			{
				tempString = input[x];
				rightString.append(tempString);
			}

			right = ((right-1)-center);
		}

		rightString = string(rightString.rbegin(), rightString.rend());

		// Test contents of rightString to make sure
		// it is valid input.
		
		if(!isDouble(rightString))
		{
			return "ERROR";
		}

		// End testing of rightString.

		arg2 = atof(rightString.c_str());

		ostringstream out;

		switch(decision)
		{
			case 1: out << (arg1 + arg2);
					break;
			case 2: out << (arg1 - arg2);
					break;
			case 3: out << (arg1 * arg2);
					break;
			case 4: if(arg2 == 0)
						return "";
					out << (arg1 / arg2);
					break;
		}

		tempString = out.str();

		erasePosition = (center-left);
		eraseCount = (right+left+1);

		input.erase(erasePosition, eraseCount);
		input.insert(erasePosition, tempString);

	} while(!notFound);
		
	return input;
}

string TextCalculator::searchString(string input, char parenth)
{
	int count, erasePosition, eraseCount, embeddedErasePosition, embeddedEraseCount, pOpen, pClose, embeddedOpen;
	string parenthString, tempString, embeddedTempString, embeddedParenthString;
	bool notFound = false;

	int stringSize;

	do
	{
		count = 0;
		stringSize = input.size();
		erasePosition = eraseCount = embeddedErasePosition = embeddedEraseCount = pOpen = pClose = embeddedOpen = 0;
		parenthString.clear();
		tempString.clear();
		embeddedTempString.clear();
		embeddedParenthString.clear();

		if(input.empty())
			return "ERROR";

		if(input[count] == '-')
			count++;

		for(; count < stringSize && input[count] != parenth; count++);
		
		if(count == stringSize && input[count-1] != parenth)
		{
			notFound = true;
			break;
		}

		pOpen = count;
		count++;

		for(; count < stringSize && input[count] != ')'; count++);

		if(count == stringSize && input[count-1] != ')')
			return "ERROR";

		pClose = count;
		count--;

		// This block of code searches for and performs mathematical operations
		// on expressions within embedded parentheses.

		for(; count > pOpen && input[count] != parenth; count--);

		if(pOpen != count)
		{
			embeddedOpen = count;

			for(int x = (embeddedOpen+1); x < pClose; x++)
			{
				embeddedTempString = input[x];
				embeddedParenthString.append(embeddedTempString);
			}

			embeddedParenthString = exponents(embeddedParenthString);
			embeddedParenthString = multiplyDivide(embeddedParenthString);

			if(divideByZeroParenthCheck(embeddedParenthString))
				return "";

			embeddedParenthString = addSubtract(embeddedParenthString);

			embeddedErasePosition = embeddedOpen;
			embeddedEraseCount = (pClose-embeddedOpen+1);

			input.erase(embeddedErasePosition, embeddedEraseCount);
			input.insert(embeddedErasePosition, embeddedParenthString);

			// After updating input, the program must return to
			// the top of the do/while loop.

			continue;

		}

		// End code for embedded parentheses

		for(int x = (pOpen+1); x < pClose; x++)
		{
			tempString = input[x];
			parenthString.append(tempString);
		}

		parenthString = exponents(parenthString);
		parenthString = multiplyDivide(parenthString);

		if(divideByZeroParenthCheck(parenthString))
				return "";

		parenthString = addSubtract(parenthString);

		erasePosition = pOpen;
		eraseCount = (pClose-pOpen+1);

		input.erase(erasePosition, eraseCount);
		input.insert(erasePosition, parenthString);

	} while(!notFound);

	return input;
}

string TextCalculator::validateInput(string input)
{
	string tempString, newString;
	int stringSize = input.size();
	int count = 0;

	if(input[count] == '-' && stringSize == 1)
		return "ERROR";

	for(; count < stringSize && input[count] != '+' && input[count] != '-' && input[count] != '/' && input[count] != '*' && input[count] != '^'; count++);

	if(count == stringSize && input[count-1] != '+' && input[count-1] != '-' && input[count-1] != '/' && input[count-1] != '*' && input[count-1] != '^')
	{
		for(int x = 0; x < stringSize; x++)
		{
			tempString = input[x];
			newString.append(tempString);
		}

		string::iterator it1 = newString.begin();
		string::iterator it2 = newString.end();

		while(it1 != it2)
		{
			*it1 = toupper(*it1);
			it1++;
		}

		if(newString == "EXIT")
			return "EXIT";
		else
			return "ERROR";
	}
	else
		return input;
}

bool TextCalculator::divideByZeroCheck(string input)
{
	if(input == "")
	{
		cout << "\tDivide by 0 error!" << endl << endl;
		return true;
	}
	else
		return false;
}

bool TextCalculator::divideByZeroParenthCheck(string input)
{
	if(input == "")
	{
		return true;
	}
	else
		return false;
}

string TextCalculator::exponents(string input)
{
	string temp = searchStringEx(input, '^');
	return temp;
}

string TextCalculator::searchStringEx(string input, char exponent)
{
	int count, center, left, right, erasePosition, eraseCount;
	double arg1, arg2;
	string leftString, rightString, tempString;
	bool notFound = false;

	int stringSize;

	do
	{
		count = 0;
		stringSize = input.size();
		center = left = right = erasePosition = eraseCount = 0;
		arg1 = arg2 = 0;
		leftString.clear();
		rightString.clear();
		tempString.clear();

		if(input.empty())
			return "ERROR";

		if(input[count] == '-')
			count++;

		for(; count < stringSize && input[count] != exponent; count++);
		
		if(count == stringSize && input[count-1] != exponent)
		{
			notFound = true;
			break;
		}

		center = count;
		count--;

		for(; count >= 0 && input[count] != '+' && input[count] != '-' && input[count] != '/' && input[count] != '*'; count--);
		
		left = count;

		if(left == 0)
		{
			int arraySize = (center - left);
			left = (center - left);

			for(int x = 0; x < arraySize; x++)
			{
				tempString = input[x];
				leftString.append(tempString);
			}
		}
		else
		{
			int arraySize = (center - (left+1));

			for(int x = (left+1); x < (arraySize+left+1); x++)
			{
				tempString = input[x];
				leftString.append(tempString);
			}

			left = (center - (left+1));
		}

		// Test contents of leftString to make sure
		// it is valid input.
		
		if(!isDouble(leftString))
		{
			return "ERROR";
		}

		// End testing of leftString.

		arg1 = atof(leftString.c_str());

		count = (center+1);

		// Allows rightString to be a negative number.
		if(input[count] == '-')
			count++;
		// End check for negative number.

		for(; count < stringSize && input[count] != '+' && input[count] != '-' && input[count] != '/' && input[count] != '*'; count++);

		right = count;

		if(count == (stringSize-1))
		{
			int arraySize = (right - center);
			
			for(int x = right; x > center; x--)
			{
				tempString = input[x];
				rightString.append(tempString);
			}

			right = (right - center);
		}
		else
		{
			int arraySize = ((right-1) - center);

			for(int x = (right-1); x > center; x--)
			{
				tempString = input[x];
				rightString.append(tempString);
			}

			right = ((right-1)-center);
		}

		rightString = string(rightString.rbegin(), rightString.rend());

		// Test contents of rightString to make sure
		// it is valid input.
		
		if(!isDouble(rightString))
		{
			return "ERROR";
		}

		// End testing of rightString.

		arg2 = atof(rightString.c_str());

		ostringstream out;

		out << pow(arg1, arg2);

		tempString = out.str();

		erasePosition = (center-left);
		eraseCount = (right+left+1);

		input.erase(erasePosition, eraseCount);
		input.insert(erasePosition, tempString);

	} while(!notFound);

	return input;
}

string TextCalculator::removeSpaces(string input)
{
	int size = input.size();
	string tempString, newString;

	if(input.empty())
		return "ERROR";

	for(int x = 0; x < size; x++)
	{
		if(input[x] != ' ')
		{
			tempString = input[x];
			newString.append(tempString);
		}
	}

	return newString;
}

bool TextCalculator::isDouble(string input)
{
	int size = input.size();
	bool value = false;
	
	for(int x = 0; x < size; x++)
	{
		if((input[x] < '0' || input[x] > '9') && input[x] != '.')
			value = false;
		else
			value = true;
	}

	return value;
}

bool TextCalculator::error(string input)
{
	bool value = false;

	if(input == "ERROR")
		{
			cout << "\tInvalid input." << endl << endl;
			value = true;
		}
	else
		value = false;

	return value;
}

bool TextCalculator::exit(string input)
{
	bool value = false;

	if(input == "EXIT")
		{
			cout << "\tGoodbye!" << endl << endl;
			value = true;
		}
	else
		value = false;

	return value;
}